package helper;
import static rayTracer.ApplicationInterface.*;
import static helper.Util.*;
import java.io.BufferedReader;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import light.DirectionLight;
import light.Light;
import light.PointLight;

import primitive3D.Box;
import primitive3D.GenericMesh;
import primitive3D.Plane;
import primitive3D.Primitive3D;
import primitive3D.Sphere;
import primitive3D.Triangle;
import rayTracer.Camera;
import rayTracer.RayTracer;
import rayTracer.Scene;
import rayTracer.Shader;
import rayTracer.Transformation;



public class SceneFileParser {
	public RayTracer rc;
	
	public SceneFileParser(RayTracer rayTracer){
		rc = rayTracer;
	}	
	
	public int parseSceneFile(String filename) throws Exception {		
		FileReader fr = null;		
		try {
			fr = new FileReader(filename);
		} catch (FileNotFoundException e) {
			throw new Exception("Scene File not Found !!");
		}
		System.out.print("\nParsing scene file ...");
		int error=0;
		String line = null;
		BufferedReader br = new BufferedReader(fr);
		try {
			while( (line = br.readLine() ) != null  ){
				line = line.trim();
				if( line.trim().length() > 0 ){										
					if( !line.startsWith("%") && !line.startsWith("//")){					
						if( line.startsWith("/*") ){
							while( !line.endsWith("*/")){
								line = br.readLine();
							}					
						}else{
							try {
								processLine(br,line);
								//error = processLine(br,line);
							} catch (Exception e) {								
								throw new Exception("Fatal Error while processing a Line !!");								
							}
							/*
							if( error != 0){
								System.out.println("Error "+error+" : "+ getErrorMessage(error));								
							}
							*/
						}
					}
				}				
			}
			
		} catch (IOException e) {
			throw new Exception("Fatal Error while reading the BufferedReader");
		}
		System.out.println("OK");
		return error;
	}



	@SuppressWarnings("unused")
	private String getErrorMessage(int error) {		
		String errorString = null;
		switch(error){
		case 1: errorString = new String( "Unknown code block" );break;
		case 2: errorString = new String( "No sc file at include line" );break;
		case 3: errorString = new String( "Image Block, width parameter missing" );break;
		case 4: errorString = new String( "Image Block, height parameter missing" );break;
		case 5: errorString = new String( "Image Block, min aa parameter missing" );break;
		case 6: errorString = new String( "Image Block, max aa parameter missing" );break;
		case 7: errorString = new String( "Image Block, samples option parameter is invalid" );break;
		case 8: errorString = new String( "Image Block, ending \"}\" is missing" );break;
		case 9: errorString = new String( "Light Block, type option parameter is missing" );break;
		case 10: errorString = new String( "Light Block, type option parameter is invalid" );break;
		case 11: errorString = new String( "Light Block, color option parameter is invalid" );break;
		case 12: errorString = new String( "Light Block, power option parameter must be a real number" );break;
		case 13: errorString = new String( "Light Block, p option parameter X is invalid" );break;
		case 14: errorString = new String( "Light Block, p option parameter Y is invalid" );break;
		case 15: errorString = new String( "Light Block, p option parameter Z is invalid" );break;
		case 16: errorString = new String( "Light Block, ending \"}\" is missing" );break;
		case 17: errorString = new String( "Shader Block, name option parameter is invalid" );break;
		case 18: errorString = new String( "Shader Block, type option parameter is missing" );break;
		case 19: errorString = new String( "Shader Block, type option parameter is invalid" );break;
		case 20: errorString = new String( "Shader Block, color option parameter is invalid" );break;
		case 21: errorString = new String( "Shader Block, diff option parameter is invalid" );break;
		case 22: errorString = new String( "Shader Block, spec option parameter (RGB) is invalid" );break;
		case 23: errorString = new String( "Shader Block, spec option parameter (Power) is invalid" );break;
		case 24: errorString = new String( "Shader Block, samples option parameter is invalid" );break;
		case 25: errorString = new String( "Shader Block, ending \"}\" is missing" );break;
		case 26: errorString = new String( "Camera Block, type option parameter is missing" );break;
		case 27: errorString = new String( "Camera Block, type option parameter is invalid" );break;
		case 28: errorString = new String( "Camera Block, eye option parameter is invalid" );break;
		case 29: errorString = new String( "Camera Block, target option parameter is invalid" );break;
		case 30: errorString = new String( "Camera Block, up option parameter is invalid" );break;
		case 31: errorString = new String( "Camera Block, fov option parameter must be a real number" );break;
		case 32: errorString = new String( "Camera Block, fdist option parameter must be a real number" );break;
		case 33: errorString = new String( "Camera Block, lensr option parameter must be a real number" );break;
		case 34: errorString = new String( "Camera Block, ending \"}\" is missing" );break;
		default: errorString = new String( "Unknown error" );
		}
		return errorString;
	}

	//Parsea los bloques principales del formato Sun Flow
	private int processLine(BufferedReader br, String line) throws Exception {
		int error=0; 
		int index;
		switch( (index=getConfigBlockId(line)) ){
			case 0: error = processInclude( line );
					break;
			case 1: error = processImage( br );
					break;
			case 2: error = processLight(br);
					break;
			case 3: Shader shader = new Shader();	
					error = processShader(br, shader);
					if( error == 0 )
						rc.shaders.add( shader );
					break;
			case 4: //Bloque Image va siempre antes que el bloque Camera
					rc.camera = new Camera(rc.width,rc.height);
					error = processCamera( br, rc.camera );					
					break;		
			case 5: processObject( br, rc.scene );			
					break; 			
			default:if( index == -1 )
						error = processUnkownOption(br);
					break;
		}		
		return error;
	}

	//Consume todo el bloque de una opcion no conocido o no soportada por este parser
	private int processUnkownOption(BufferedReader br) throws IOException {
		
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
		while( line != null && !line.startsWith("}") ){					
		    if( (line = br.readLine() ) != null )
		    	line = line.trim();		    
		}
		 if( line == null )
		    return 1000;
		return 0;
		
	}

	
	private int processObject(BufferedReader br, Scene scene) throws Exception {
		int error = 0;
		Scanner scanner=null;
		String name=null;
		String shaderString = null;		
		Transformation trans = null;
		int objectTypeIndex = -1;
		
		Primitive3D figure=null;
		Point3d position=null;
		double radius=0;
		Point3d p=null;
		Vector3d n=null;
		int cantMeshPoints = 0;
		Point3d[] meshPoints = null;
		int cantMeshTriangles = 0;
		ArrayList<Primitive3D>meshTriangles = null;
		double minX = Double.POSITIVE_INFINITY;
		double minY = Double.POSITIVE_INFINITY;
		double minZ = Double.POSITIVE_INFINITY;
		
		double maxX = Double.NEGATIVE_INFINITY;
		double maxY = Double.NEGATIVE_INFINITY;
		double maxZ = Double.NEGATIVE_INFINITY;
		
		Point3d bbMin = new Point3d(minX, minY, minZ);
		Point3d bbMax = new Point3d(maxX, maxY, maxZ);
		
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
					
		while( line != null && !line.startsWith("}") ){			
			line = skipComments(br, line);
			scanner = new Scanner(line.trim());
		    scanner.useDelimiter(espacios);
		    double[] values = new double[3];
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		      
		      if( option.trim().compareTo("shader") == 0 ){		    	  		    	  
		    	  if( scanner.hasNext() )
		    		  shaderString = scanner.next();
		    	  else
		    		  return 716; 
		      }	
		      //Para procesar la opcion transform
		      int index = line.indexOf("{");
		      if( index > 0 ){
		    	  String subBlockNameString = line.substring(0, index).trim();
		    	  if( subBlockNameString.compareTo("transform") == 0 ){
		    		  trans = new Transformation();
		    		  if( ( error = processObjectTransform( br, scene, trans ) ) != 0 )
		    		  	return error;
		    	  }
		      }	
					
		      if( option.trim().compareTo("type") == 0 ){
		    	  String type=null;
		    	  boolean typeFound = false;		    	  
		    	  if( scanner.hasNext() )
		    		  type = scanner.next();
		    	  else
		    		  return 724;		    	  
		    	  for( int i=0 ; !typeFound && i<objectTypeStrings.length ; i++){
		    		  if( type.compareTo(objectTypeStrings[i]) == 0){
		    			  objectTypeIndex = i;
		    			  typeFound = true;
		    		  }
		    	  }
		    	  if( !typeFound )
		    		  return 725;		    	  
		      }	   
		      
		      if( option.trim().compareTo("name") == 0 ){		    	  		    	  
		    	  if( scanner.hasNext() )
		    		  name = scanner.next();
		    	  else
		    		  return 716;		    	 		    	 
		      }	
		      
		      if( option.trim().compareTo("c") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 726;		    	 
		    	  position = new Point3d(values);
		      }
		     
		      if( option.trim().compareTo("r") == 0 ){
		    	  if( scanner.hasNextDouble() )
		    		  radius = scanner.nextDouble();
		    	  else
		    		  return 729;
		      }
		      
		      if( option.trim().compareTo("p") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 730;		    	 
		    	   p= new Point3d(values);
		      }
		      if( option.trim().compareTo("n") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 731;		    	 
		    	   n= new Vector3d(values);
		      }
		      if( option.trim().compareTo("points") == 0 ){
		    	  if( scanner.hasNextInt() ){
		    		  cantMeshPoints = scanner.nextInt();
		    		  meshPoints = new Point3d[cantMeshPoints];
		    		  if( getMeshPoints( br, meshPoints ) != 0 )
		    			  return 732;
		    	  }else
		    		  return 733;
		      }
		      //Se asume que "points" vienen siempre antes que "triangles"
		      if( option.trim().compareTo("triangles") == 0 ){
		    	  if( scanner.hasNextInt() ){
		    		  cantMeshTriangles = scanner.nextInt();		    		  
		    		  meshTriangles = new ArrayList<Primitive3D>(cantMeshTriangles);
		    		  Shader shader = getShaderReference(shaderString);
		    		  if( getMeshTriangles( br, meshPoints, meshTriangles, 
		    				  			cantMeshTriangles, shader, bbMin, bbMax ) != 0 )
		    			  return 733;
		    	  }else
		    		  return 734;
		      }
		      
		    }		   
		    if( ( line = br.readLine()) != null )
		    	line = line.trim();
		}
	    if( line == null )
	    	return 777;
	    switch(objectTypeIndex){
	    	case 0 :figure = new Sphere(position,radius);	    			
	    			break;
	    	case 1 :figure = new Box(getShaderReference(shaderString));
	    			break;
	    	case 2 :figure = new Plane(p, n);
	    			break;
	    	case 3 :figure = new GenericMesh( meshTriangles, bbMin, bbMax);	    			
	    			break;	    
	    }

	    if( objectTypeIndex != -1 ){	    
	    	setModificators(figure, shaderString, name, trans);	    	    	
		    scene.add(figure);		    
	    }   
	    scanner.close();		
		return error;		
	}
	
	private String skipComments(BufferedReader br, String line) throws IOException {		
		while( line.startsWith("%") || line.startsWith("//"))
			if( ( line = br.readLine() ) != null )
				line = line.trim();
		
		while( line.startsWith("/*") ){
			while( line != null && !line.endsWith("*/")){
				line = br.readLine();
			}	
			if( ( line = br.readLine() ) != null )
				line = line.trim();
		}
		if( line != null ){			
			if( line.startsWith("%") || line.startsWith("//") )
				skipComments(br, line);
			
		}
		return line;
	}

	private int getMeshTriangles(BufferedReader br, Point3d[]meshPoints, 
										ArrayList<Primitive3D>meshTriangles,
										int cantMeshTriangles, Shader shader,
										Point3d bbMin, Point3d bbMax ) 
															throws IOException {
		int i;
		Scanner scanner = null;
		String line = null;
		
		for( i=0 ; i< cantMeshTriangles ; i++ ){
			line = br.readLine();
			if( line != null ){
				scanner = new Scanner(line.trim());
			    scanner.useDelimiter(espacios);
			    int[] values = new int[3];
			    if( getPointIndexes(scanner, values) != 0 )
			    	return 892;
			    Point3d points[] = new Point3d[]{ 	meshPoints[values[0]],
			    									meshPoints[values[1]],
			    									meshPoints[values[2]]
			    								};
			    Triangle t = new Triangle( points, shader );
			    if( t.bb.min.x < bbMin.x ) bbMin.x = t.bb.min.x;
			    if( t.bb.min.y < bbMin.y ) bbMin.y = t.bb.min.y;
			    if( t.bb.min.z < bbMin.z ) bbMin.z = t.bb.min.z;
			    
			    
			    if( t.bb.max.x > bbMax.x )	bbMax.x = t.bb.max.x;
			    if( t.bb.max.y > bbMax.y )	bbMax.y = t.bb.max.y;
			    if( t.bb.max.z > bbMax.z )	bbMax.z = t.bb.max.z;
			    
			    meshTriangles.add( t );
			}		    
		}
		if( i < meshTriangles.size() )
			return 893;
		return 0;
	}

	
	private int getPointIndexes(Scanner scanner, int[] values) {
		if( scanner.hasNextInt() )
	   		  values[0] = scanner.nextInt();
	   	  else
	   		  return 600;
	   	  if( scanner.hasNextInt() )
	   		  values[1] = scanner.nextInt();
	   	  else
	   		  return 601;
	   	  if( scanner.hasNextInt() )
	   		  values[2] = scanner.nextInt();
	   	  else
	   		  return 602;
	   	  
	   	  return 0;
	}


	private int getMeshPoints(BufferedReader br, Point3d[] meshPoints) 
															throws IOException {
		int i;
		Scanner scanner = null;
		String line = null;
		for( i=0 ; i< meshPoints.length ; i++ ){
			line = br.readLine();
			if( line != null ){
				scanner = new Scanner(line.trim());
			    scanner.useDelimiter(espacios);
			    double[] values = new double[3];
			    if( getPointConfiguration(scanner, values) != 0 )
			    	return 890;
			    meshPoints[i] = new Point3d(values);
			}		    
		}
		if( i < meshPoints.length )
			return 891;
		return 0;
	}


	private void setModificators(Primitive3D figure, String shaderString, 
										String name, Transformation trans ) {		
		figure.shader = getShaderReference(shaderString);
		if( name != null )
			figure.name = new String(name);
		
		if( trans != null ){	
			figure.transform(trans);			
	    }		
	}


	private int processObjectTransform(BufferedReader br, Scene scene2, Transformation trans) throws IOException {
		Scanner scanner = null;
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
		while( line != null && !line.startsWith("}") ){			
			line = skipComments(br, line);
			scanner = new Scanner(line.trim());
		    scanner.useDelimiter(espacios);
		    double[] values = new double[3];
		    
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		      
		      if( option.trim().compareTo("rotatex") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.rotX( scanner.nextDouble() );
		    	  else
		    		  return 132; 
		      }	
		      if( option.trim().compareTo("rotatey") == 0 ){		    
		    	  if( scanner.hasNextDouble() ){
		    		  double d = scanner.nextDouble();		    		  
		    		  trans.rotY( d );
		    	  }else
		    		  return 133; 
		      }	
		      if( option.trim().compareTo("rotatez") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.rotZ( scanner.nextDouble() );
		    	  else
		    		  return 134; 
		      }	
		      if( option.trim().compareTo("scalex") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.scaleX( scanner.nextDouble() );
		    	  else
		    		  return 135; 
		      }	
		      if( option.trim().compareTo("scaley") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.scaleY( scanner.nextDouble() );
		    	  else
		    		  return 136; 
		      }	
		      if( option.trim().compareTo("scalez") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.scaleZ( scanner.nextDouble() );
		    	  else
		    		  return 137; 
		      }	
		      if( option.trim().compareTo("scaleu") == 0 ){		    	  		    	  
		    	  if( scanner.hasNextDouble() )
		    		  trans.scaleU( scanner.nextDouble() );
		    	  else
		    		  return 138; 
		      }	
		      if( option.trim().compareTo("translate") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 139;		    	 
		    	  trans.translate(values);
		      }
		    }
		    if( (line = br.readLine() ) != null )
		    	line = line.trim();		    
		}
		 if( line == null )
		    return 888;
		return 0;
	}


	private Shader getShaderReference(String shaderString) {		
		for( int i=0 ; i<rc.shaders.size() ; i++ )
			if( shaderString.compareTo(rc.shaders.get(i).name) == 0 ){				
				return rc.shaders.get(i);
			}		
		return null;
	}


	private int processCamera(BufferedReader br, Camera camera) throws IOException {
		int error = 0;
		Scanner scanner=null;
		
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();		
		while( line != null && !line.startsWith("}") ){			
			line = skipComments(br, line);
			scanner = new Scanner(line.trim());
		    scanner.useDelimiter(espacios);
		    double[] values = new double[3];
		    
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		      
		      if( option.trim().compareTo("type") == 0 ){
		    	  String type=null;
		    	  boolean typeFound = false;
		    	  
		    	  if( scanner.hasNext() )
		    		  type = scanner.next();
		    	  else
		    		  return 26;
		    	  
		    	  for( int i=0 ; i<cameraTypeStrings.length ; i++){
		    		  if( type.compareTo(cameraTypeStrings[i]) == 0){
		    			  camera.type = cameraTypeCodes[i];
		    			  typeFound = true;
		    		  }
		    	  }
		    	  if( !typeFound )
		    		  return 27;
		      }	   
		     
		      if( option.trim().compareTo("eye") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 28;		    	 
		    	  camera.eyePosition = new Point3d(values);
		      }
		      if( option.trim().compareTo("target") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 29;		    	 
		    	  camera.canvasPosition = new Point3d(values);
		      }
		      if( option.trim().compareTo("up") == 0 ){		    	 
		    	  if( getPointConfiguration(scanner, values) != 0 )
		    		  return 30;		    	 
		    	  camera.setCameraVerticalReference(new Vector3d(values));
		      }
		      if( option.trim().compareTo("fov") == 0 ){
		    	  if( scanner.hasNextDouble() ){
		    		  camera.setVisualAngle( scanner.nextDouble() );		    
		    	  }
		    	  else
		    		  return 31;
		      }
		      if( option.trim().compareTo("fdist") == 0 ){
		    	  if( scanner.hasNextDouble() )
		    		  camera.fdist = scanner.nextDouble();
		    	  else
		    		  return 32;
		      }
		      if( option.trim().compareTo("lensr") == 0 ){
		    	  if( scanner.hasNextDouble() )
		    		  camera.lensr= scanner.nextDouble();
		    	  else
		    		  return 33;
		      }
		    }		   
		    if( ( line = br.readLine() ) != null )
		    	line = line.trim();
		}
	    if( line == null )
	    	return 34;	    
	    scanner.close();		
		return error;		
	}
	
	private int getPointConfiguration(Scanner scanner, double[] values) {
		
   	  if( scanner.hasNextDouble() )
   		  values[0] = scanner.nextDouble();
   	  else
   		  return 600;
   	  if( scanner.hasNextDouble() )
   		  values[1] = scanner.nextDouble();
   	  else
   		  return 601;
   	  if( scanner.hasNextDouble() )
   		  values[2] = scanner.nextDouble();
   	  else
   		  return 602;
   	  
   	  return 0;
	}


	private int processShader(BufferedReader br, Shader shader) throws IOException {
		int error = 0;
		Scanner scanner=null;
		
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
		
		while( line != null && !line.startsWith("}") ){	
			line = skipComments(br, line);
			scanner = new Scanner(line.trim());
		    scanner.useDelimiter(espacios);
		    
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		      
		      float[]values = new float[4];
		      if( option.trim().compareTo("name") == 0 ){
		    	  String name=null;		    	  
		    	  if( scanner.hasNext() )
		    		  name = scanner.next();
		    	  else
		    		  return 17;
		    	  
		    	 shader.name = name;		    	 
		      }	
		      
		      if( option.trim().compareTo("type") == 0 ){
		    	  String type=null;
		    	  boolean typeFound = false;
		    	  
		    	  if( scanner.hasNext() )
		    		  type = scanner.next();
		    	  else
		    		  return 18;
		    	  
		    	  for( int i=0 ; i<shaderTypeStrings.length ; i++){
		    		  if( type.compareTo(shaderTypeStrings[i]) == 0){
		    			  shader.shaderType = shaderTypeCodes[i];
		    			  typeFound = true;
		    		  }		    		  
		    	  }
		    	  if( !typeFound )
		    		  return 19;
		      }	   
		      if( option.trim().compareTo("color") == 0 ){			    	  
		    	  if( !getColorConfiguration( values, scanner.nextLine() ) )
		    		  return 20;		    	  
		    	  shader.color = values;
		      }
		      if( option.trim().compareTo("diff") == 0 ){		    	  
		    	  if( !getColorConfiguration( values, scanner.nextLine() ) )
		    		  return 21;
		    	  shader.diff = values;
		      }
		      if( option.trim().compareTo("spec") == 0 ){		    	  
		    	  String cad = scanner.nextLine();
		    	  int index = cad.indexOf("}");
		    	  if( index == -1 )
		    		  return 22;
		    	  String colorString = cad.substring(0, index+1).trim();
		    	  String specPowerString = cad.substring(index+1).trim();		    	  
		    	  if( !getColorConfiguration( values, colorString ) )
		    		  return 23;
		    	  shader.spec = values;
		    	  if( specPowerString != null || specPowerString.trim().length() > 0 ){
		    		  try {
		    			  shader.specPower = Integer.parseInt(specPowerString);
		    		  } catch (Exception e) {					
		    			  return 23;
		    		  }
		      	  }
		      }
		      if( option.trim().compareTo("samples") == 0 ){
		    	  if( scanner.hasNextInt() )
		    		  shader.samples = scanner.nextInt();
		    	  else
		    		  return 24;
		      }
		      //Para shader mirror
		      if( option.trim().compareTo("refl") == 0 ){			    	  
		    	  if( !getColorConfiguration( values, scanner.nextLine() ) )
		    		  return 244;		    	  
		    	  shader.refl = values;
		      }
		      //Para shader glass
		      if( option.trim().compareTo("eta") == 0 ){
		    	  if( scanner.hasNextFloat() )
		    		 shader.refractIndex = scanner.nextFloat();
		    	  else
		    		  return 12;
		      }
		    }		   
		    if( ( line = br.readLine() ) != null )
		    	line = line.trim();
		}
	    if( line == null )
	    	return 25;
	    
	    if( shader.shaderType == SHADER_TYPE.CONSTANT ){
	    	shader.reflectK = 0;
	    	shader.refractK = 0;
	    }else if( shader.shaderType == SHADER_TYPE.MIRROR ){
	    	shader.reflectK = 1;
	    	shader.refractK = 0;
	    }else if( shader.shaderType == SHADER_TYPE.GLASS ){
	    	shader.refractK = 1;
	    	shader.color[0] = 0.2f;
	    }
	    scanner.close();		
		return error;		
	}
	
	private int processLight(BufferedReader br) throws IOException {
		int error = 0;
		Scanner scanner=null;
		Light light=null;
		int lightType=-1;
		float lightPower=0;
		Point3d lightPosition=null;
		float[] colorValues=null;
		float intensity=0;
		Vector3d dir=null;
		Point3d source=null;
		Point3d target=null;
		
		@SuppressWarnings("unused")
		float radius=0;		
		float[] emitValues=null;
		
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
		
		while( line != null && !line.startsWith("}") ){	
			line = skipComments(br, line);
			scanner = new Scanner(line.trim());
		    scanner.useDelimiter(espacios);
		    
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		      
		      if( option.trim().compareTo("type") == 0 ){
		    	  String type=null;
		    	  boolean typeFound = false;
		    	  
		    	  if( scanner.hasNext() )
		    		  type = scanner.next();
		    	  else
		    		  return 8;
		    	  
		    	  for( int i=0 ; !typeFound && i<lightTypeStrings.length ; i++){
		    		  if( type.compareTo(lightTypeStrings[i]) == 0){
		    			  lightType = i;
		    			  typeFound = true;
		    		  }
		    	  }
		    	  if( !typeFound )
		    		  return 10;
		      }	   
		      if( option.trim().compareTo("color") == 0 ){	
		    	  colorValues = new float[4];
		    	  if( !getColorConfiguration( colorValues, scanner.nextLine() ) )
		    		  return 11;		    	  
		      }
		      if( option.trim().compareTo("power") == 0 ){
		    	  if( scanner.hasNextFloat() )
		    		  lightPower = scanner.nextFloat();
		    	  else
		    		  return 12;
		      }
		      if( option.trim().compareTo("p") == 0 ){
		    	  double x, y, z;
		    	  if( scanner.hasNextDouble() )
		    		  x = scanner.nextDouble();
		    	  else
		    		  return 13;
		    	  if( scanner.hasNextDouble() )
		    		  y = scanner.nextDouble();
		    	  else
		    		  return 14;
		    	  if( scanner.hasNextDouble() )
		    		  z = scanner.nextDouble();
		    	  else
		    		  return 15;
		    	  lightPosition = new Point3d(x, y, z);
		      }
		      //Para luz direccional 
		      if( option.trim().compareTo("source") == 0 ){
		    	  double x, y, z;
		    	  if( scanner.hasNextDouble() )
		    		  x = scanner.nextDouble();
		    	  else
		    		  return 13;
		    	  if( scanner.hasNextDouble() )
		    		  y = scanner.nextDouble();
		    	  else
		    		  return 14;
		    	  if( scanner.hasNextDouble() )
		    		  z = scanner.nextDouble();
		    	  else
		    		  return 15;
		    	  source = new Point3d(x, y, z);
		      }
		      if( option.trim().compareTo("target") == 0 ){
		    	  double x, y, z;
		    	  if( scanner.hasNextDouble() )
		    		  x = scanner.nextDouble();
		    	  else
		    		  return 13;
		    	  if( scanner.hasNextDouble() )
		    		  y = scanner.nextDouble();
		    	  else
		    		  return 14;
		    	  if( scanner.hasNextDouble() )
		    		  z = scanner.nextDouble();
		    	  else
		    		  return 15;
		    	  target = new Point3d(x, y, z);
		      }
		      if( option.trim().compareTo("radius") == 0 ){
		    	  if( scanner.hasNextFloat() )
		    		  radius = scanner.nextFloat();
		    	  else
		    		  return 12;
		      }
		      if( option.trim().compareTo("emit") == 0 ){	
		    	  emitValues = new float[4];
		    	  if( !getColorConfiguration( emitValues, scanner.nextLine() ) )
		    		  return 11;		    	  
		      }
		      if( option.trim().compareTo("intensity") == 0 ){
		    	  if( scanner.hasNextFloat() )
		    		  intensity = scanner.nextFloat();
		    	  else
		    		  return 12;
		      }
		    }		   
		    if( ( line = br.readLine() ) != null )
		    	line = line.trim();
		}
	    if( line == null )
	    	return 16;
	    
	    switch(lightType){
	    	case 0 :light = new PointLight(lightPosition);
	    			((PointLight)light).iD = lightPower;
	    			((PointLight)light).iS = lightPower;
	    			((PointLight)light).color = colorValues;
	    			break;
	    	case 1 :dir = getVector(source, target);	    			
	    			light = new DirectionLight(dir);
	    			light.color = emitValues;
	    			light.iD = light.iD * intensity;
	    			light.iS = light.iS * intensity;
					break;
	    }	    
	    rc.lights.add(light);
	    scanner.close();		
		return error;		
	}
	
	private boolean getColorConfiguration(float[]values, String string) {
		if( !string.trim().startsWith("{") || !string.trim().endsWith("}") )
			return false;
		
		Scanner scanner = new Scanner(string.substring(2, string.length()-1).trim() );
	    scanner.useDelimiter(comillas);
	    values[0] = 1;
	    if( scanner.hasNext()){
	    	@SuppressWarnings("unused")
			String cad = scanner.next();//Consumo el tipo RGB, siempre linear	    		    	
	    	scanner.useDelimiter(espacios);
	    	cad =scanner.next();//Consumo la doble comilla que quedo	    	
	    	if( scanner.hasNext() ){	    		
	    		values[1] = scanner.nextFloat();	    		
	    	}
	    	else    		
	    		return false;	    	
	    	if( scanner.hasNextDouble() ){
	    		values[2] =  scanner.nextFloat();	    		
	    	}else
	    		return false;
	    	if( scanner.hasNextDouble() ){
	    		values[3] = scanner.nextFloat();	    		
	    	}
	    	else
	    		return false;
	    }
	    
		return true;
	}
	
	private int processImage(BufferedReader br) throws IOException {	
		int error = 0;
		Scanner scanner=null;
		String line;
		if( ( line = br.readLine() ) != null )
			line = line.trim();
		
		while( line != null && !line.startsWith("}") ){		
			line = skipComments(br, line);
			scanner = new Scanner(line);
		    scanner.useDelimiter(espacios);
		    
		    if ( scanner.hasNext() ){
		      String option = scanner.next();		    
		      if( option.trim().compareTo("resolution") == 0 ){
		    	  if( scanner.hasNextInt() )
		    		  rc.width = scanner.nextInt();
		    	  else
		    		  return 3;
		    	  if( scanner.hasNextInt() )
		    		  rc.height = scanner.nextInt();
		    	  else
		    		  return 4;
		      }	   
		      if( option.trim().compareTo("aa") == 0 ){
		    	  if( scanner.hasNextInt() )
		    		  rc.aaMin= scanner.nextInt();
		    	  else
		    		  return 5;
		    	  if( scanner.hasNextInt() )
		    		  rc.aaMax = scanner.nextInt();
		    	  else
		    		  return 6;
		      }
		      if( option.trim().compareTo("samples") == 0 ){
		    	  if( scanner.hasNextInt() )
		    		  rc.aaSamples = scanner.nextInt();
		    	  else
		    		  return 7;
		      }
		    }		   
		    if( ( line = br.readLine() ) != null )
		    	line = line.trim();
		}
	    if( line == null )
	    	return 8;
	    
	    scanner.close();
		return error;
	}
	
	private int processInclude(String line) throws Exception {
		int error = 0;
		Scanner scanner = new Scanner(line);
	    scanner.useDelimiter(espacios);
	    if ( scanner.hasNext() ){
	      scanner.next(); //consumo la cadena "include"
	      String otherFile = scanner.next();
	      if( otherFile == null )
	    	  return 2;
	      error = parseSceneFile( otherFile.trim() );
	    }	   
	    scanner.close();		
		return error;
	}

	private int getConfigBlockId(String line) {
		int index = line.indexOf("{");		
		if( index > 0 ){
			String blockNameString = line.substring(0, index).trim();			
			for( int i=1 ; i< codeBlockStrings.length ; i++ ){
				if( blockNameString.compareTo(codeBlockStrings[i]) == 0 ){		    		
					return i;
		    	}
			}	    	
		}else{
			if( line.startsWith("include ") ){				
				return 0;
			}else
				return -2;
		}
		
		return -1;		
	}
	
	public String toString(){		
		StringBuffer str = new StringBuffer();		
		str.append("PARSED PARAMETERS :  \n");
		str.append("Image :  \n");
		str.append("width = "+rc.width+"\theight = "+rc.height+"\n");
		str.append("fov = "+rc.imageFov+"\n");
		str.append("aa minValue = " + rc.aaMin + "  ;  \taa MaxValue = " +  rc.aaMax+"  ;  \tSamples="+rc.aaSamples+"\n");		
		str.append("Lights: \n"+rc.lights+"\n");
		str.append("Shaders: \n"+rc.shaders+"\n");
		str.append("Scene camera: \n"+rc.camera+"\n");
		str.append("SCENE : \n"+rc.scene+"\n");
		return str.toString();
	}
}


